﻿//*** Guidance generated code ***//
using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.Linq;
using System.Text;
using System.Web.Mvc;

using Survey.Common;
using Survey.Common.Validation;
using Survey.Common.Business;
using Survey.Common.Data;

using ProjectBase.Core;
using ProjectBase.Core.Tools.Import;

namespace Survey.Business
{
    /// <summary>
    ///     Powerfull project based Mother Facade.
    ///     Every stuff which is project dependent and common for alle entities
    ///     should go here
    /// </summary>
    /// <typeparam name = "TEntity">Entity</typeparam>
    /// <typeparam name = "TID"></typeparam>
    /// <typeparam name = "TDao"></typeparam>
    /// <typeparam name = "TFilter"></typeparam>
    public abstract class BaseFacade<TEntity, TID, TFilter, TDao>
        : ReadFacade<TEntity, TID, TFilter, TDao>
        , IBaseFacade<TEntity, TID, TFilter>
        where TEntity : class, IPersistentObjectWithTypedId<TID>
        where TDao : class, IBaseDao<TEntity, TID, TFilter>
        where TFilter : class, IFilter<TID>, new()
    {
        #region members
        static readonly IList<EventHandler> Changes = new List<EventHandler>();
        static readonly object Locker = new object();
        #endregion members

        #region constructor
        protected BaseFacade()
        {
            CheckFileDependency();
        }
        #endregion constructor

        #region Factory Method
        public override TFilter CreateNewFilter()
        {
            return new TFilter();
        }
        #endregion Factory Method

        #region Can be deleted, edited
        public virtual bool CanBeEdited(TEntity entity)
        {
            return EntityValidator.CanBeEdited(entity);
        }
        public virtual bool CanBeDeleted(TEntity entity)
        {
            return EntityValidator.CanBeDeleted(entity);
        }
        #endregion Can be deleted, edited

        #region IWriteFacade

        /// <summary>
        ///     Provides basic call to Dao to insert new object.
        ///     If any error occurs, Messages are filled and IsError = true
        /// </summary>
        /// <param name = "entity"></param>
        /// <returns></returns>
        public virtual TEntity Add(TEntity entity)
        {
            if (EntityValidator.Validate(entity, ValidateOn.Add, this)
                && !Messages.ContainsError())
            {
                var t = Dao.Add(entity);
                OnChange();
                return t;
            }
            return entity;
        }

        /// <summary>
        ///     Provides basic call to Dao to update existing object.
        ///     If any error occurs, Messages are filled and IsError = true
        /// </summary>
        /// <param name = "entity"></param>
        /// <returns></returns>
        public virtual TEntity Update(TEntity entity)
        {
            var filter = CreateNewFilter();
            filter.MaxRowsPerPage = 1;
            filter.SetIdSearch(entity.ID);
            filter.PropertyList[Str.Common.ID] = null;

            if (!Dao.Find(filter).Any()) // need to check if there is still any
            {
                this.PublishError(Str.Messages.CannotUpdate1, entity.ToDisplay());
                this.PublishInfo(Str.Messages.EntityNotExists2, entity.IDToDisplay, entity.ToDisplay());
                return entity;
            }

            // OK
            if (EntityValidator.Validate(entity, ValidateOn.Update, this)
                && !Messages.ContainsError())
            {
                entity = Dao.Update(entity);
                OnChange();
            }
            return entity;
        }

        /// <summary>
        ///     Provides basic call to Dao to delete existing object.
        ///     If any error occurs, Messages are filled and IsError = true
        /// </summary>
        /// <param name = "entity"></param>
        public virtual void Delete(TEntity entity)
        {
            var filter = CreateNewFilter();
            filter.MaxRowsPerPage = 1;
            filter.PropertyList[Str.Common.ID] = null;

            if (!Dao.Find(filter).Any()) // need to check if there is still any
            {
                this.PublishWarning(Str.Messages.EntityNotExists2, entity.IDToDisplay, entity.ToDisplay());
                this.PublishError(Str.Messages.CannotDelete1, entity.ToDisplay());
            }

            // OK
            if (EntityValidator.Validate(entity, ValidateOn.Delete, this)
                && !Messages.ContainsError())
            {
                Dao.Delete(entity);
                OnChange();
            }
        }
        #endregion IWriteFacade

        #region IBinder
        public virtual bool BindEntity(TEntity entity
            , IValueProvider valueProvider
            , string prefix
            , IEnumerable<string> includeProperties
            , IEnumerable<string> excludeProperties = null)
        {
            return Binder.Bind(entity, valueProvider, prefix, includeProperties, excludeProperties);
        }
        #endregion IBinder

        #region Validation Rules
        public override string ValidationRulesToString(string validationType)
        {
            var result = EntityValidator.ValidationRulesToString(validationType);
            if (result.IsNotEmpty())
            {
                return result;
            }
            return base.ValidationRulesToString(validationType);
        }
        public IEntityValidator<TEntity> EntityValidator { protected get; set; }
        #endregion Validation Rules

        #region Import
        /// <summary>
        /// Expects the source file (Excel) to be passed, and imported
        /// </summary>
        /// <param name="dataSource"></param>
        /// <param name="processor"></param>
        public virtual bool Import(byte[] dataSource, Action<IValueProvider> processor = null)
        {
            return ImportProvider.Execute<TEntity, TID>(dataSource, processor);
        }
        public virtual IImportProvider ImportProvider { protected get; set; }
        #endregion Import

        #region DeleteAll
        /// <summary>
        /// Deletes all items, meeting searching criteria (including the RowCount - only current page)
        /// </summary>
        /// <param name="filter"></param>
        public virtual bool DeleteAll(TFilter filter)
        {
            filter.PropertyList[Str.Common.ID] = null;

            var count = 0;
            var deletedItems = new StringBuilder();
            foreach (var item in Find(filter).ToList())
            {
                deletedItems.Append("{0}; ".FormatWith(item.IDToDisplay));
                Delete(GetById(item.ID));
                count++;
            }
            if (Messages.ContainsError())
            {
                this.PublishWarning(Str.Messages.DeleteAllWasNotSuccessful2
                    , count
                    , deletedItems.ToString());
                return false;
            }
            this.PublishInfo(Str.Messages.DeleteAllWasSuccessful2
                , count
                , deletedItems.ToString());
            return true;
        }
        #endregion DeleteAll

        #region Change event handler
        /// <summary>
        ///     The list of static methods which sould be called to clear the list
        /// </summary>
        protected readonly FileDependency StaticDependency = new FileDependency(Global.Settings.FileCacheStaticPath);

        public static event EventHandler ChangeHandler
        {
            add
            {
                if (!value.Method.IsStatic)
                {
                    return;
                }
                lock (Locker)
                {
                    if (!Changes.Contains(value))
                    {
                        Changes.Add(value);
                    }
                }

                ResetCacheFacade.ResetAll = value;
            }
            remove
            {
                lock (Locker)
                {
                    if (Changes.Contains(value))
                    {
                        Changes.Remove(value);
                    }
                }
            }
        }

        /// <summary>
        ///     Attention! Only static method-delegates can be appended to that handler
        ///     After Add, Update, Delete operations this event is called
        /// </summary>
        public virtual event EventHandler Change
        {
            add { ChangeHandler += value; }
            remove { ChangeHandler -= value; }
        }

        /// <summary>
        ///     Rise this event to clear all static-caches
        /// </summary>
        protected void OnChange()
        {
            this.PublishDebug("BaseFacade calls OnChange()");

            if (Changes.IsEmpty())
            {
                return;
            }

            StaticDependency.Invalidate(); // change file timestamp

            foreach (var change in Changes)
            {
                change(this, new EventArgs());
            }
        }

        protected void CheckFileDependency()
        {
            if (Global.Settings.AllowFileCacheDependencyForStatic
                && StaticDependency.HasChanged)
            {
                OnChange();
            }
        }
        #endregion Change event handler
    }
}